
#ifndef __VFS_H__
#define __VFS_H__

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#if CONFIG_LITTLEFS
#include "vfs_lfs.h"
#endif

typedef enum {
#if CONFIG_LITTLEFS
    VFS_TYPE_LFS,
#endif
#if CONFIG_FATFS
    VFS_TYPE_FATFS,
#endif
    VFS_TYPE_MAX
} vfs_type_e;

typedef struct {
    int dd_fd;		/* directory file */
    int dd_loc;		/* position in buffer */
    int dd_seek;
    char *dd_buf;	/* buffer */
    int dd_len;		/* buffer length */
    int dd_size;	/* amount of data in buffer */
    int dd_lock;
} DIR;

struct dirent {
	unsigned long	d_fileno;
	unsigned short	d_reclen;
	unsigned short	d_namlen;
	char		d_name[256];
};

typedef struct {
    FILE *(*fopen)(const char *path, const char *mode);
    int (*fclose)(FILE * fp);
    size_t (*fread)(void *__restrict buf, size_t size, size_t count, FILE *__restrict fp);
    size_t (*fwrite)(const void *__restrict buf, size_t size, size_t count, FILE * fp);
    int (*fflush)(FILE * fp);
    int (*fseek)(FILE *fp, long offset, int whence);
    long (*ftell)(FILE * fp);
    void (*rewind)(FILE * fp);
    int (*mkdir)(const char *path, mode_t mode);
    DIR	*(*opendir)(const char *path);
    int (*closedir)(DIR *dp);
    struct dirent *(*readdir)(DIR *dp);
    void (*seekdir)(DIR *dp, long loc);
    long (*telldir)(DIR *dp);
    void (*rewinddir)(DIR *dp);
    int (*chdir)(const char *path);
    char *(*getcwd)(char *buf, size_t size);
    int (*rmdir)(const char *path);
    int (*access)(const char *path, int mode);
    int (*stat)(const char *path, struct stat *buf);
    int (*remove)(const char *path);
    int (*rename)(const char *oldpath, const char *newpath);
} vfs_ops_t;

typedef struct {
    int id;
    char *name;
    vfs_type_e type;
    union {
    #if CONFIG_LITTLEFS
        lfs_object_t lfs_obj;
    #endif
    #if CONFIG_FATFS
        int fatfs_obj; // occupied by fatfs, will complete later
    #endif
        int occupied;
    } u;
    vfs_ops_t *ops;
} vfs_t;

#if CONFIG_LITTLEFS
extern const vfs_ops_t vfs_lfs_ops;
#define __lfs __attribute__((unused, section(".lfsRegTable")))
#define vfs_lfs_export(vfs_name, vfs_id, start_addr, end_addr) vfs_t _vfs_lfs_##vfs_id __lfs = \
    { \
        .id = vfs_id, \
        .name = vfs_name, \
        .type = VFS_TYPE_LFS, \
        .u.lfs_obj.cfg_ctx = {{0}, (int)start_addr, (int)end_addr}, \
        .ops = (vfs_ops_t *)&vfs_lfs_ops, \
    }
#endif

#if CONFIG_FATFS
extern const vfs_ops_t vfs_fatfs_ops;
#define __fatfs __attribute__((unused, section(".fatfsRegTable")))
#define vfs_fatfs_export(vfs_name, vfs_id) vfs_t _vfs_fatfs_##vfs_id __lfs = \
    { \
        .id = vfs_id, \
        .name = vfs_name, \
        .type = VFS_TYPE_FATFS, \
        .u.fatfs_obj = 0, \
        .ops = (vfs_ops_t *)&vfs_fatfs_ops, \
    }
#endif

int vfs_init(void);
vfs_t *vfs_get_by_path(const char *path, char **fs_path, char **abs_path);

#endif
